package no.ntnu.fp.mmi;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;

import no.ntnu.fp.su.Event;
import no.ntnu.fp.su.Events;
import no.ntnu.fp.su.Filter;
import no.ntnu.fp.su.Log;
import no.ntnu.fp.su.LogEntry;
import no.ntnu.fp.su.Platform;
import no.ntnu.fp.su.Watch;
import no.ntnu.fp.su.WatchWell;

@SuppressWarnings("serial")
public class WatchModel extends JPanel {

	public static final String WellProperty = "ActiveWell";
	public static final String WellListProperty = "wellList";
	public static final String EventProperty = "Event";
	public static final String EventListProperty = "EventList";
	public final String addToWellListProperty = "addWell";

	private PropertyChangeSupport pcs;
	private ArrayList<WatchWell> wellList;
	private Platform platform;
	private WatchWell activeWell;
	private GraphingDataModel graph;
	private Event activeEvent;
	private Events eventList;
	private Watch watch;
	private Timer timer;

	public Timer getTimer() {
		return timer;
	}

	/**
	 * The constructor that makes a WatchModel for a Watch
	 * 
	 * @param Watch
	 *            - The WatchSystem
	 */
	public WatchModel(Watch watch) {
		this.watch = watch;
		this.platform = watch.getPlatform();
		pcs = new PropertyChangeSupport(this);
		this.setEventList(platform.getEvents());
		this.setWellList(platform.getWells());
		this.activeWell = getWellList().get(0);

		System.out.println("m�");

		graph = new GraphingDataModel();
		graph.loadStartValues(activeWell);
		PollDataTimer pdt = new PollDataTimer();
		timer = new Timer();

		timer.scheduleAtFixedRate(pdt, 1000, watch.isWatch() ? 1000
				: Log.DB_WRITE_BUFFER_TIME * 1000);

		WatchPanel gui = new WatchPanel(this);
		gui.setVisible(true);
	}

	/**
	 * Returns this platform
	 */
	public Platform getPlatform() {
		return platform;
	}

	/**
	 * Returns the wellList for this WatchModel
	 */
	public ArrayList<WatchWell> getWellList() {
		return this.wellList;
	}

	/**
	 * Sets the wellList for this WatchModel
	 * 
	 * @param ArrayList
	 *            <WatchWell> contains the wellList
	 */
	public void setWellList(ArrayList<WatchWell> list) {
		this.wellList = list;
	}

	/**
	 * Adds the well to the wellList of this WatchModel
	 * 
	 * @param WatchWell
	 *            to add to the wellList
	 */
	public void addWelltoList(WatchWell newWell) {
		int old = this.wellList.size();
		this.wellList.add(newWell);

		pcs
				.firePropertyChange(addToWellListProperty, old, this.wellList
						.size());
	}

	/**
	 * Returns the eventList of this WatchModel
	 */
	public Events getEventList() {
		return this.eventList;
	}

	/**
	 * Sets the eventList of this WatchModel
	 * 
	 * @param Events
	 *            - the list to set
	 */
	public void setEventList(Events list) {
		this.eventList = list;
	}

	/**
	 * Returns the activeWell of this WatchModel
	 */
	public WatchWell getActiveWell() {
		return activeWell;
	}

	/**
	 * Returns the Watch of this model
	 */
	public Watch getWatch() {
		return watch;
	}

	/**
	 * Sets the active well of this WatchModel
	 * 
	 * @param WatchWell
	 */
	public void setActiveWell(WatchWell active) {
		WatchWell old = this.activeWell;
		this.activeWell = active;
		graph.loadStartValues(active);
		pcs.firePropertyChange(WellProperty, old, active);

	}

	/**
	 * Returns the graph of this WatchModel
	 */
	public GraphingDataModel getGraph() {
		return graph;
	}

	/**
	 * Returns the activeEvent of this WatchModel
	 */
	public Event getEvent() {
		return activeEvent;
	}

	/**
	 * Sets the activeEvent of this WatchModel
	 * 
	 * @param Event
	 */
	public void setActiveEvent(Event setev) {
		Event old = this.activeEvent;
		this.activeEvent = setev;
		pcs.firePropertyChange(EventProperty, old, setev);
	}

	/**
	 * Adds the propertyChangeListener to this WatchModel
	 * 
	 * @param PropertyChangeListener
	 */
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		System.out.println("add pcl");
		if (pcs != null) {
			System.out.println("adding pcl on " + pcs.toString());
			pcs.addPropertyChangeListener(listener);
			System.out.println("listeners after add "
					+ pcs.getPropertyChangeListeners().length);
		}
	}

	/**
	 * The Poll timer that updates the graph
	 */
	class PollDataTimer extends TimerTask {
		private Timestamp tid;

		public PollDataTimer() {
			tid = new Timestamp(0);
		}

		@Override
		public void run() {
			// number of data points in the graph
			int graphsize = GraphingDataModel.GRAPHSIZE;
			if (activeWell == null)
				return;
			// Polling data from wells to the graphmodel if it is the watch
			if (watch.isWatch()) {
				LogEntry l = activeWell.getLog().getLatest();
				if (l == null)
					return;
				if (graph.getD1().size() > graphsize)
					graph.getD1().remove(0);
				graph.getD1().add(l.getR());
				if (graph.getD3().size() > graphsize)
					graph.getD3().remove(0);
				graph.getD3().add(l.getT() - GraphingDataModel.D3MIN);
				if (graph.getD4().size() > graphsize)
					graph.getD4().remove(0);
				graph.getD4().add(l.getP());
				// Polling for masterwatch from log in larger intervals
			} else {
				if (Log.compareTime(activeWell.getLog().getLatest().getTime(),
						tid) > 0) {

					List<LogEntry> logentries = activeWell.getLog().getLog(
							Filter.ALL).subList(
							Math.max(activeWell.getLog().getLog(Filter.ALL)
									.indexOf(
											activeWell.getLog().getlogByTime(
													tid)), Math.max(activeWell
									.getLog().getLog(Filter.ALL).size()
									- Log.DB_WRITE_BUFFER_TIME, 0)),
							activeWell.getLog().getLog(Filter.ALL).size());
					for (LogEntry le : logentries) {
						if (graph.getD1().size() > graphsize)
							graph.getD1().remove(0);
						graph.getD1().add(le.getR());
						if (graph.getD3().size() > graphsize)
							graph.getD3().remove(0);
						graph.getD3().add(le.getT() - GraphingDataModel.D3MIN);
						if (graph.getD4().size() > graphsize)
							graph.getD4().remove(0);
						graph.getD4().add(le.getP());
					}
					tid = activeWell.getLog().getLatest().getTime();
				}

			}
			graph.update();
			//property change listener didn't work, so had to do a little fix for repainting the graph
			((WatchPanel) pcs.getPropertyChangeListeners()[0]).repaint();
			pcs.firePropertyChange("graph", 0, 0);

		}
	}

	/**
	 * Returns the propertyChangeSupport of this WatchModel
	 */
	public PropertyChangeSupport getPCS() {
		return pcs;
	}

}
